package xmlsync2;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import datawave.util.LoggerCache;

/**
 * @author jinwoo
 *
 * To change this generated comment edit the template variable "typecomment":
 * Window>Preferences>Java>Templates.
 * To enable and disable the creation of type comments go to
 * Window>Preferences>Java>Code Generation.
 */
public class Session
{
	private String url;
	private String user;
	private String password;
	private String driver;
	private Connection connection;
	private DatabaseMetaData metadata;
	private HashMap proceduretable;
	private SessionPool ownerpool;
	private int timeout;
	private String encoding = null;
	private static Logger log = LoggerCache.get(Session.class.getName());
	static private boolean statementcache = checkcache();

	static private boolean checkcache()
	{
		String cache = System.getProperty("xmlsync.statement.cache");
		boolean result = true;
		if (cache != null && cache.equalsIgnoreCase("false")) 
		{
			log.info("xmlsync statment caching function is turned off.");
			return false;
		}
		else
		{
			log.info("xmlsync statment caching function is turned on.");
		 	return true;
		}
	}
	
	public int getcachedstatementcount()
	{
		return proceduretable.size();
	}
	
	public Session(final String dsn, SessionPool ownerpool, final int timeout) throws XmlsyncException
	{
		this.timeout = timeout;
		this.ownerpool = ownerpool;
		url = Str.extract("url", dsn);
		user = Str.extract("user", dsn);
		password = Str.extract("password", dsn);
		driver = Str.extract("driver", dsn);
		this.encoding = Str.extract("encoding", dsn);

		proceduretable = new HashMap();
		try
		{
			DriverManager.setLoginTimeout(timeout);
			connection = DriverManager.getConnection(this.url, this.user, this.password);
			metadata = connection.getMetaData();
		}
		catch (SQLException e)
		{
			Object[] args = {url, e.toString()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0009"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	public Connection getconnection()
	{
		return connection;
	}

	public SessionPool ownerpool()
	{
		return ownerpool;
	}
	
	public boolean isactive()
	{
		try
		{
			if(connection.isClosed()) 
				return false;
			else return true;
		}
		catch (SQLException e)
		{
			return false;
		}	
	}
	
	public void close()
	{
		try
		{
			closestatement();
			connection.close();
		}
		catch (SQLException e)
		{}
	}
	
	public void commit() throws SQLException
	{
		if (!connection.getAutoCommit())
			connection.commit();
		connection.clearWarnings();
	}
	
	public void rollback() throws SQLException
	{
		if (!connection.getAutoCommit())
			connection.rollback();
		connection.clearWarnings();
		close(); 
	}
	
	private void closestatement()
	{
		Iterator it = proceduretable.keySet().iterator();
		while (it.hasNext())
		{
			String key = (String)it.next();
			Procedure procedure = (Procedure)proceduretable.get(key);
			procedure.close();
		}
	}
	
	public QueryResult executesp(final String sp, final LinkedList param) throws XmlsyncException
	{
		Procedure procedure = (Procedure)proceduretable.get(sp);
		if (procedure == null)
		{
			procedure = createprocedure(sp);
			if (statementcache) 
			{
				log.info("'"+sp+"' statement is cahced.");
				proceduretable.put(sp, procedure);
			}
		}
		try
		{
			procedure.setparameter(param);
			QueryResult result = procedure.execute();
			if (!statementcache) 
			{
				log.info("statement caching is disabled.");
				procedure.close();
			}
			return result;
		}
		catch (SQLException e)
		{
			Object[] args = {sp, e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0010"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	public QueryResult executequery(final String query) throws XmlsyncException
	{
		QueryResult result = new QueryResult(encoding);
		Statement statement = createstatement();
		LinkedList list = Str.buildlist(query, ";");
		try
		{
			statement.setQueryTimeout(timeout);
			result.addrecordset(null);
			for (int i=0; i < list.size(); i ++)
			{
				String sentence = (String)list.get(i);
				if (sentence.substring(0, 6).equalsIgnoreCase("select"))
				{
					ResultSet resultset = executeselect(statement, sentence);
					if (resultset != null)
					{
						result.addrecordset(resultset);
						resultset.close();
					}
				}
				else executeupdate(statement, sentence);
			}
			statement.close();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0011"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
		return result;
	}
	
	private void executeupdate(Statement statement, final String sentence) throws XmlsyncException
	{
		try
		{
			if (encoding == null)
				statement.executeUpdate(sentence);
			else statement.executeUpdate(new String(sentence.getBytes("EUC-KR"), this.encoding));			
		}
		catch (UnsupportedEncodingException e)
		{
			Object[] args = {this.encoding};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0007"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
		catch (SQLException e)
		{
			close(); 
			Object[] args = {sentence, e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0012"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

	private ResultSet executeselect(Statement statement, final String sentence) throws XmlsyncException
	{
		try
		{		
			if (encoding == null)
				return statement.executeQuery(sentence);
			else return statement.executeQuery(new String(sentence.getBytes("EUC-KR"), this.encoding));
		}
		catch (UnsupportedEncodingException e)
		{
			Object[] args = {this.encoding};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0007"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
		catch (SQLException e)
		{
			close(); 
			Object[] args = {sentence, e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0012"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	private Procedure createprocedure(final String sp) throws XmlsyncException
	{
		String catalog = Str.catalogof(sp);
		String spname = Str.procedurenameof(sp);
		try
		{
			return new Procedure(sp, connection, getmetaproc(catalog, spname), getmetaparam(catalog, spname), timeout, encoding);
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {catalog, spname, e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0013"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	private ResultSet getmetaproc(final String catalog, final String spname) throws XmlsyncException
	{
		try
		{
			if (catalog == null)
				return metadata.getProcedures(null, user.toUpperCase(), spname.toUpperCase());
			else return metadata.getProcedures(catalog.toUpperCase(), user.toUpperCase(), spname.toUpperCase());
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {spname, e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0014"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	private ResultSet getmetaparam(final String catalog, final String spname) throws XmlsyncException
	{
		try
		{
			if (catalog == null)
				return metadata.getProcedureColumns(null, user.toUpperCase(), spname.toUpperCase(), null);
			else return metadata.getProcedureColumns(catalog.toUpperCase(), user.toUpperCase(), spname.toUpperCase(), null);
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {spname, e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0015"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	private Statement createstatement() throws XmlsyncException
	{
		Statement statement;
		try
		{
			statement =
				connection.createStatement(
					ResultSet.TYPE_FORWARD_ONLY,
					ResultSet.CONCUR_READ_ONLY);
			statement.setQueryTimeout(timeout);
			statement.setFetchDirection(ResultSet.FETCH_FORWARD);
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0016"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
		return statement;
	}
	/**
	 * Method getmaxconnection.
	 * @return int
	 */
	public int getmaxconnection() throws XmlsyncException
	{
		try
		{
			return	metadata.getMaxConnections();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0017"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	/**
	 * Method getmaxcachedstatement.
	 * @return int
	 */
	public int getmaxstatements() throws XmlsyncException
	{
		try
		{
			return metadata.getMaxStatements();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0018"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

	/**
	 * Method getdatabaseproductname.
	 * @return String
	 */
	public String getdatabaseproductname() throws XmlsyncException
	{
		try
		{
			return metadata.getDatabaseProductName();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0018"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	/**
	 * Method getdatabaseversion.
	 */
	public String getdatabaseversion() throws XmlsyncException
	{
		try
		{
			return metadata.getDatabaseProductVersion();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0019"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

	/**
	 * Method supporttransaction.
	 * @return boolean
	 */
	public boolean supporttransaction() throws XmlsyncException
	{
		try
		{
			return metadata.supportsTransactions();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0020"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

	/**
	 * Method supportstoredprocedure.
	 * @return boolean
	 */
	public boolean supportstoredprocedure() throws XmlsyncException
	{
		try
		{
			return metadata.supportsStoredProcedures();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0021"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

	/**
	 * Method supportstoredprocedure.
	 * @return boolean
	 */
	public String getdrivername() throws XmlsyncException
	{
		try
		{
			return metadata.getDriverName();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0022"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}
	
	public String getdriverversion() throws XmlsyncException
	{
		try
		{
			return metadata.getDriverVersion();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0023"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

	public boolean supportsmultipleresultsets() throws XmlsyncException
	{
		try
		{
			return metadata.supportsMultipleResultSets();
		}
		catch (SQLException e)
		{
			close();
			Object[] args = {e.getMessage()};
			String msg = MessageFormat.format(Messages.getString("XMLSYNC0024"), args);
			log.error(msg);
			throw new XmlsyncException(msg);
		}
	}

}
